﻿// Released to the public domain. Use, modify and relicense at will.
//
// F# Bing Translator Sample using F# Async Programming
// This file is designed for use in F# Interactive

let myAppId = "07CF8..."
// Replace this string by your AppId
// If you don't have Bing appId, you can freely create one:
//   http://www.bing.com/developers/createapp.aspx

open System.IO
open System.Net
open System.Windows.Forms
open System.Drawing
open System.Text

let show x = printfn "%A" x

let detectUri       = "http://api.microsofttranslator.com/V1/Http.svc/Detect?appId=" + myAppId
let translateUri    = "http://api.microsofttranslator.com/V1/Http.svc/Translate?appId=" + myAppId
let languageUri     = "http://api.microsofttranslator.com/V1/Http.svc/GetLanguages?appId=" + myAppId
let languageNameUri = "http://api.microsofttranslator.com/V1/Http.svc/GetLanguageNames?appId=" + myAppId

let run x = Async.RunSynchronously x

// Like the 'httpAsync' functions, but read the lines, not just the text
let httpLines (uri:string) = async { 
    let request = WebRequest.Create uri 
    use! response = request.AsyncGetResponse()          
    use stream = response.GetResponseStream()
    use reader = new StreamReader(stream)
    let lines = [ while  not reader.EndOfStream do yield reader.ReadLine() ]
    return lines 
  }

// Find out which languages are supported
httpLines languageUri |> run |> show

let languages =  httpLines languageUri |> run 

type System.Net.WebRequest with
    /// An extension member to write content into an WebRequest.
    /// The write of the content is synchronous.
    member req.WriteContent (content:string) =
        let bytes = Encoding.UTF8.GetBytes content
        req.ContentLength <- int64 bytes.Length
        use stream = req.GetRequestStream()
        stream.Write(bytes,0,bytes.Length) 

    /// An extension member to read the content from a response to a WebRequest.
    /// The read of the content is synchronous once the response has been received.
    member req.AsyncReadResponse () =
        async { use! response = req.AsyncGetResponse()
                use responseStream = response.GetResponseStream()
                use reader = new StreamReader(responseStream)
                return reader.ReadToEnd() }

// Now, an asynchronous version of the same
let detectLanguage text = async { 
    let request = WebRequest.Create (detectUri, Method="Post", ContentType="text/plain")
    do request.WriteContent text
    return! request.AsyncReadResponse() 
  }

detectLanguage "This is part of the F# Tutorial" |> run |> show

// Next, let's play around with the translation API
let translateText (text, fromLang, toLang) = async {
    let uri = sprintf "%s&from=%s&to=%s" translateUri fromLang toLang
    let request2 = WebRequest.Create (uri, Method="Post", ContentType="text/plain")
    do request2.WriteContent text
    return! request2.AsyncReadResponse()
  }

// This wraps up the calls to the detection and translation APIs
let detectAndTranslate (text, toLang) = 
  async {
    try
      let! fromLang = detectLanguage text 
      let! translated = translateText (text, fromLang, toLang)
      return (fromLang, toLang, text, translated)
    with exn ->
      let msg = sprintf "error: %A" exn.Message
      return ("", toLang, text, msg)
  }

#time 
                
let text = "Hello, are you ready to learn some parallel I/O programming?"

Async.Parallel 
   [ for lang in languages -> translateText (text, "en", lang) ] 
   |> run 
   |> show


let form       = new Form (Visible=true, TopMost=true, Height=500, Width=600)
let textBox    = new TextBox (Width=450, Text=text, Font=new Font("Consolas", 14.0F))
let button     = new Button (Text="Translate", Left = 460)
let translated = new TextBox (Width=590, Height=400, Top=50, ScrollBars=ScrollBars.Both, Multiline=true,
                              Font=new Font("Consolas", 14.0F))

form.Controls.Add textBox
form.Controls.Add button
form.Controls.Add translated

button.Click.Add(fun args ->
    translated.Text <- "Translating..."

    let ctxt = System.Threading.SynchronizationContext.Current
    let text = textBox.Text
    let asyncList =
        [for lang in languages -> 
          async { let! (fromLang, toLang, text, translatedValue) = detectAndTranslate (text, lang)
                  do! Async.SwitchToContext ctxt
                  let res = sprintf "\r\n%s --> %s: \"%s\"" fromLang toLang translatedValue
                  translated.Text <- translated.Text + res } ]
    asyncList |> Async.Parallel |> Async.Ignore |> Async.Start
 )
